<?php
/**
 * Model for course table in database
 *
 * @author Dat Nguyen <datnt@evolable.asia>
 */
class Course extends AppModel {
	var $useTable = 'courses';
	var $primaryKey = 'id';
	var $name = 'Course';

	var $belongsTo = array(
		'Category' => array(
			'className' => 'Category',
			'foreignKey' => 'category',
		),
		'Area' => array(
			'className' => 'Area',
			'foreignKey' => 'area',
		),
		'CourseType' => array(
			'className' => 'CourseType',
			'foreignKey' => 'course_type',
		),
	);
	var $hasMany = array(
		'CourseDetail' => array(
			'className' => 'CourseDetail',
			'foreignKey' => 'course_id',
			'conditions'    => array('CourseDetail.del_flg' => '0'),
		)
	);

	var $validate = array(
		'title' => array(
			'rule' => array('minlength', 1),
			'required' => true,
			'message' => Messages::course_title_is_required,
		),
		'description' => array(
			'rule' => array('minlength', 1),
			'required' => true,
			'message' => Messages::course_description_is_required,
		),
		'target_user' => array(
			'rule' => array('minlength', 1),
			'required' => true,
			'message' => Messages::course_target_is_required,
		),
		'price' => array(
			'numeric' => array(
				'rule' => 'numeric',
				'required' => true,
				'message' => Messages::course_price_is_required,
			),
			'minvalue' => array(
				'rule' => array('comparison', '>=', 1),
				'message' => Messages::price_must_be_large_than_0
			)
		),
		'course_image01' => array(
			'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')),
			'required' => true,
			'message' => Messages::course_image_01_is_not_valid,
		),
	);

	function validates() {
		if (isset ($this->data['Course']['id']) && $this->data['Course']['id']) {
			unset($this->validate['course_image01']);
		}
		return parent::validates();
	}

	/**
	 * Upload image to server
	 *
	 * @return bool True if process successfully, else false
	 */
	function processImage() {
		$alias = $this->alias;
		$data = $this->data[$alias];
		$validatedType = Configure::read('Course.type');
		$datePath = date('Y/m/d');
		//create directory if does not exist
		$dirNames = explode('/', trim($datePath));
		$dir = Configure::read('Course.upload_dir_prefix');
		if (!is_dir($dir)) {
			mkdir($dir, 0777);
		}
		foreach ($dirNames as $name) {
			if (empty($name)) {
				continue;
			}
			$dir .= DS . $name;
			if (!is_dir($dir)) {
				mkdir($dir, 0777);
			}
		}
		App::import('lib', 'ImageUtil');
		//valid and upload file if data is valid
		$maxFileSize = intval(Configure::read('Course.max_icon_image_size'));
		for ($i = 1; $i <= 3; $i++) {
			$field = sprintf('course_image%02d', $i);
			if (isset($data[$field]['error']) && $data[$field]['error'] == 0) {
				if (in_array($data[$field]['type'], $validatedType)) {
					if ($data[$field]['size'] < $maxFileSize) {
						list($fileName, $extension) = $this->hashFileName($data[$field]['name']);
						$fileDir = $dir . DS . $fileName . '.' . $extension;
						$thumbName = $fileName . self::THUMB_SUFIX . '.' . $extension;
						$thumbFileDir = $dir . DS . $thumbName;
						if ($result = move_uploaded_file($data[$field]['tmp_name'], $fileDir)) {
							chmod($fileDir, 0777);
							$image = new ImageUtil($fileDir);
							$image->extendResize(Configure::read('Course.width_icon_image'), Configure::read('Course.height_icon_image'));
							$image->save($thumbFileDir);
							chmod($thumbFileDir, 0777);
							$this->data[$alias][$field] = $datePath. '/' . $thumbName;
						} else {
							$this->data[$alias][$field] = '';
						}
					} else {
						$this->validationErrors[$field] = sprintf(Messages::course_image_is_large_than_2MB, $i);
					}
				} else {
					$this->validationErrors[$field] = sprintf(Messages::course_image_is_not_valid, $i, implode(', ', $validatedType));
				}
			} else if (isset($data[$field]['error']) && $data[$field]['error'] == 1) {
				$this->validationErrors[$field] = sprintf('Course image %02d could not be uploaded.', $i);
			} else {
				$this->data[$alias][$field] = '';
			}
		}
		if (is_array($this->validationErrors) && count($this->validationErrors)) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * Get default option for course list paginator
	 *
	 * @return array
	 */
	function getPaginatorOption() {
		$options = array();
		$options['joins'] = array(
			array(
				'table' => 'profiles',
				'alias' => 'Profile',
				'type' => 'LEFT',
				'conditions' => array(
					'Course.user_id = Profile.user_id AND Profile.del_flg = 0'
				)
			)
		);
		$options['fields'] = array(
			'Course.*',
			'Category.name',
			'Category.rank',
			'Area.name',
			'CourseType.type',
			'Profile.*',
		);
		$options['conditions'] = array(
			'Course.del_flg' => 0,
			'Course.display_flg' => 1,
		);
		$options['limit'] = Configure::read('page_limit');

		$options['order'] = 'Course.modified DESC';

		return $options;
	}

	/**
	 * Override afterSave function of Model, using for save tag of course
	 * @param bool $created
	 */
	function afterSave($created) {
		parent::afterSave($created);
		if ($this->id) {
			if (is_string($this->data['Course']['tag'])) {
				$tags = explode(',', $this->data['Course']['tag']);
			} elseif (is_array($this->data['Course']['tag'])) {
				$tags = $this->data['Course']['tag'];
			}
			foreach ($tags as $key => $tag) {
				$tags[$key] = trim($tag);
			}
			App::import('model', 'Tag');
			App::import('model', 'Tagmap');
			$tagObj = new Tag();
			$options = array(
				'conditions' => array(
					'Tag.name' => $tags,
				)
			);
			$tagmap = new Tagmap();
			$tagmap->deleteCourseTag($this->id);
			$existingTags = $tagObj->find('list', $options);
			foreach ($tags as $tag) {
				$tagId = false;
				foreach ($existingTags as $key => $existingTag) {
					if ($existingTag == $tag) {
						$tagId = $key;
						break;
					}
				}
				if (!$tagId) {
					$data['Tag'] = array(
						'id' => null,
						'name' => $tag,
					);
					$tagObj->save($data);
					$tagId = $tagObj->id;
				}
				$mapData['Tagmap'] = array(
					'id' => null,
					'course_id' => $this->id,
					'tag_id' => $tagId,
				);
				$tagmap->save($mapData);
			}
		}
	}

	/**
	 * Update del_flg field in database
	 *
	 * @param mixed $courseId, id of course or the list of course's id
	 * @param $value, the value will be updated to, 1 or 0
	 */
	function updateDelFlg($courseId, $value) {
		$conditions = array(
			'Course.id' => $courseId,
		);
		//make sure value is 0 or 1
		if ($value != 0) {
			$value = 1;
		}

		$this->updateAll(array('del_flg' => $value), $conditions);
		App::import('model', 'CourseDetail');
		$conditions = array(
			'CourseDetail.course_id' => $courseId,
		);
		$courseDetail = new CourseDetail();
		$courseDetail->updateAll(array('del_flg' => $value), $conditions);

		App::import('model', 'EnteredCourse');
		$enterCourse = new EnteredCourse();
		$conditions = array(
			'EnteredCourse.detail_id IN (SELECT id FROM courses_detail WHERE course_id = '.intval($courseId).')',
		);
		$enterCourse->updateAll(array('del_flg' => $value), $conditions);
	}

	/**
	 * Mass update display_flg field in database
	 *
	 * @param $courseId
	 * @param $value (0,1)
	 */
	function updateDisplayFlg($courseId, $value) {
		$conditions = array(
			'Course.id' => $courseId,
		);
		//make sure value is 0 or 1
		if ($value != 0) {
			$value = 1;
		}

		$this->updateAll(array('display_flg' => $value), $conditions);
	}

	/**
	 * Get a course detail have opening time closest to the current time
	 *
	 * @param $courseId
	 * @return mixed
	 */
	function getClosestCourseDetail($courseId) {
		$options = array(
			'conditions' => array(
				'CourseDetail.course_id' => $courseId,
				'CourseDetail.opening_date >=' => date('Y-m-d H:i:s'),
				'CourseDetail.del_flg' => 0,
			),
			'order' => 'CourseDetail.opening_date ASC',
			'fields' => array(
				'CourseDetail.*',
				'CourseCounting.entered_counting',
				'Area.name'
			),
			'recursive' => 0,
			'joins' => array(
				array(
					'table' => '(SELECT count(ec.user_id) AS entered_counting, ec.detail_id FROM entered_courses ec WHERE ec.del_flg = 0 GROUP BY ec.detail_id)',
					'alias' => 'CourseCounting',
					'type' => 'LEFT',
					'conditions' => array(
						'CourseDetail.id = CourseCounting.detail_id'
					)
				)
			)
		);

		$courseDetails = $this->CourseDetail->find('all', $options);

		$details = array();
		$totalCourse = count($courseId);
		foreach ($courseDetails as $detail) {
			$courseId = $detail['CourseDetail']['course_id'];
			if (!isset($details[$courseId])) {
				$details[$courseId] = $detail;
				$totalCourse--;
			}
			if ($totalCourse == 0) {
				break;
			}
		}

		return $details;
	}

	/**
	 * Get detail of course by id
	 *
	 * @param $courseId
	 * @param $detailId
	 * @return mixed
	 */
	function getCourseDetail($courseId, $detailId) {
		$options = array(
			'conditions' => array(
				'CourseDetail.course_id' => $courseId,
				'CourseDetail.id' => $detailId,
				'CourseDetail.opening_date >=' => date('Y-m-d H:i:s'),
				'CourseDetail.del_flg' => 0,
			),
			'order' => 'CourseDetail.opening_date ASC',
			'fields' => array(
				'CourseDetail.*',
				'CourseCounting.entered_counting',
				'Area.name'
			),
			'recursive' => 0,
			'joins' => array(
				array(
					'table' => '(SELECT count(ec.user_id) AS entered_counting, ec.detail_id FROM entered_courses ec WHERE ec.del_flg = 0 GROUP BY ec.detail_id)',
					'alias' => 'CourseCounting',
					'type' => 'LEFT',
					'conditions' => array(
						'CourseDetail.id = CourseCounting.detail_id'
					)
				)
			)
		);

		return $this->CourseDetail->find('first', $options);
	}

	/**
	 * Get course information by it details id
	 *
	 * @param $ids The list id of course details
	 * @return mixed. Array of course if found, false if not found
	 */
	function getCourseByDetail($ids) {
		$options = array();
		$options['recursive'] = -1;
		$options['joins'] = array(
			array(
				'table' => 'courses_detail',
				'alias' => 'CourseDetail',
				'type' => 'INNER',
				'conditions' => array(
					'Course.id = CourseDetail.course_id'
				)
			)
		);
		$options['fields'] = array(
			$this->alias . '.id',
			$this->alias . '.title',
			'CourseDetail.id',
		);
		$options['conditions'] = array(
			'CourseDetail.id' => $ids,
		);

		return $this->find('all', $options);
	}

	/**
	 * Get news course for display on index page
	 *
	 * @param int $limit Number of course will be get
	 * @return array
	 */
	function getNewCourse($limit = 3) {
		$options = $this->getPaginatorOption();
		$options['joins'][] = array(
			'table' => 'recommend_courses',
			'alias' => 'RecommendCourse',
			'type' => 'INNER',
			'conditions' => array(
				$this->alias . '.id = RecommendCourse.course_id'
			)
		);
		$options['order'] = 'RecommendCourse.rank';
		if ($limit) {
			$options['limit'] = $limit;
		} else {
			$options['limit'] = 3;
		}
		return $this->find('all', $options);
	}

	/**
	 * Get course information for edit
	 */
	function getForEdit($id) {
		$options = array();
		$options['fields'] = array($this->alias.'.*');
		$options['recursive'] = -1;
		$options['conditions'] = array(
			$this->alias.'.id' => $id,
		);
		$course = $this->find('first', $options);
		$options = array();
		$options['fields'] = array('id', 'name');
		$options['conditions'] = array(
			'id IN (SELECT tag_id FROM tagmap WHERE course_id = ' . intval($id) . ')',
		);
		App::import('model', 'Tag');
		$tag = new Tag();
		$tags = $tag->find('list', $options);
		$course['Course']['tag'] = implode(',', $tags);
		return $course;
	}

	/**
	 * Get course information for enter course, leave
	 */
	function getForEnter($id) {
		$options['conditions'] = array(
			$this->alias . '.' . $this->primaryKey => $id,
			$this->alias . '.del_flg' => 0,

		);
		$options['fields'] = array(
			'Course.*',
			'Category.name',
			'Area.name',
			'CourseType.type',
			'Profile.first_name',
			'Profile.last_name',
			'Profile.headline',
			'Profile.icon_image',
			'Profile.description',
		);
		$options['joins'] = array(
			array(
				'table' => 'profiles',
				'alias' => 'Profile',
				'type' => 'LEFT',
				'conditions' => array(
					'Course.user_id = Profile.user_id AND Profile.del_flg = 0'
				)
			)
		);

		return $this->find('first', $options);
	}

	/**
	 * Remove old image of course
	 * @param $imagePath
	 */
	function removeOldImage($imagePath) {
		$fileDir = Configure::read('Course.upload_dir_prefix').$imagePath;
		if (is_file($fileDir)) {
			unlink($fileDir);
		}
	}
	
	/**
	 * 
	 * @param int $id
	 * @param array $fields
	 * @return array
	 */
	function getCourseInforById($id, $fields) {
		return $this->find('first', array(
			'fields' => $fields,
			'conditions' => array(
				$this->name.'.id' => $id
			)
		));
	}

	/**
	 * Get course information for view course detail
	 *
	 * @param array $options
	 * @return mixed
	 */
	function getForView($options = array()) {
		$default = array();
		$default['fields'] = array(
			$this->alias . '.*',
			'Category.name',
			'Category.rank',
			'Area.name',
			'CourseType.type',
			'Profile.user_id',
			'Profile.first_name',
			'Profile.last_name',
			'Profile.headline',
			'Profile.icon_image',
			'Profile.description',
			'User.facebook_user_id',
		);
		$default['joins'] = array(
			array(
				'table' => 'profiles',
				'alias' => 'Profile',
				'type' => 'LEFT',
				'conditions' => array(
					$this->alias . '.user_id = Profile.user_id AND Profile.del_flg = 0'
				)
			),
			array(
				'table' => 'users',
				'alias' => 'User',
				'type' => 'INNER',
				'conditions' => array(
					$this->alias . '.user_id = User.id',
				)
			)
		);
		$options = array_merge($default, $options);

		return $this->find('first', $options);
	}

	/**
	 * Get condition for get relate course functions
	 *
	 * @param $field
	 * @param $fieldValue
	 * @param $exclusiveIds
	 * @return array
	 */
	function getOptionsForRelated($field, $fieldValue, $exclusiveIds) {
		$options = array(
			'fields' => array('Course.id', 'Course.title'),
			'conditions' => array(
				$this->alias.'.display_flg' => 1,
				$this->alias.'.del_flg' => 0,
				'NOT' => array(
					$this->alias.'.id' => $exclusiveIds
				)),
			'recursive' => -1,
			'limit' => 5,
			'order' => $this->alias.'.modified DESC',
		);

		if ($this->hasField($field)) {
			$options['conditions'][$this->alias.'.'.$field] = $fieldValue;
		}

		return $options;
	}

	/**
	 * Get related courses by category
	 *
	 * @param $categoryId
	 * @param $exclusiveIds
	 * @return mixed
	 */
	function getRelatedByCategory($categoryId, $exclusiveIds) {
		$options = $this->getOptionsForRelated('category', $categoryId, $exclusiveIds);

		return $this->find('list', $options);
	}

	/**
	 * Get related courses by user id
	 *
	 * @param $userId
	 * @param $exclusiveIds
	 * @return mixed
	 */
	function getRelatedByUser($userId, $exclusiveIds) {
		$options = $this->getOptionsForRelated('user_id', $userId, $exclusiveIds);

		return $this->find('list', $options);
	}

	/**
	 * Get related course by area
	 *
	 * @param $areaId
	 * @param $exclusiveIds
	 * @return mixed
	 */
	function getRelatedByArea($areaId, $exclusiveIds) {
		$options = $this->getOptionsForRelated('area', $areaId, $exclusiveIds);

		return $this->find('list', $options);
	}

	/**
	 * Check if user is owner of course or not
	 *
	 * @param $courseId
	 * @param $userId
	 * @return bool
	 */
	function isOwner ($courseId, $userId) {
		$options = array(
			'conditions' => array(
				$this->alias . '.id' => $courseId,
				$this->alias . '.user_id' => $userId,
			)
		);
		if ($this->find('count', $options)) {
			return true;
		} else {
			return false;
		}
	}

	function isJoined ($courseId, $userId) {
		$options = array(
			'conditions' => array(
				$this->alias . '.id' => $courseId,
				'EnteredCourse.user_id' => $userId,
				'EnteredCourse.del_flg' => 0,
			),
			'joins' => array(
				array(
					'table' => 'courses_detail',
					'alias' => 'CourseDetail',
					'type' => 'INNER',
					'conditions' => array(
						$this->alias . '.id = CourseDetail.course_id',
					)
				),
				array(
					'table' => 'entered_courses',
					'alias' => 'EnteredCourse',
					'type' => 'INNER',
					'conditions' => array(
						'CourseDetail.id = EnteredCourse.detail_id',
						'EnteredCourse.del_flg = 0',
					)
				)
			)
		);

		if ($this->find('count', $options)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Get list the course have not approve of user
	 *
	 * @param int $userId
	 * @return array
	 */
	function getWaitingCourse($userId) {
		$options = array(
			'conditions' => array(
				$this->alias . '.user_id' => $userId,
				$this->alias . '.display_flg' => 0,
				$this->alias . '.del_flg' => 0,
			),
			'recursive' => -1,
		);

		return $this->find('all', $options);
	}

	/**
	 * Get list course user has entered
	 *
	 * @param $userId
	 * @return array
	 */
	function getPassCourse($userId) {
		$options = array(
			'fields' => array(
				'DISTINCT ' .$this->alias . '.id',
				$this->alias . '.title',
			),
			'joins' => array(
				array(
					'table' => 'courses_detail',
					'alias' => 'CourseDetail',
					'type' => 'INNER',
					'conditions' => array(
						$this->alias . '.id = CourseDetail.course_id',
						'CourseDetail.del_flg = 0',
						'CourseDetail.closing_date < "' .date('Y-m-d H:i:s') . '"',
					)
				),
				array(
					'table' => 'entered_courses',
					'alias' => 'EnteredCourse',
					'type' => 'INNER',
					'conditions' => array(
						'CourseDetail.id = EnteredCourse.detail_id',
						'EnteredCourse.del_flg = 0',
					)
				),
				array(
					'table' => 'reviews',
					'alias' => 'Review',
					'type' => 'LEFT',
					'conditions' => array(
						$this->alias . '.id = Review.course_id',
						'Review.del_flg = 0',
					)
				)
			),
			'recursive' => -1,
			'conditions' => array(
				'EnteredCourse.user_id' => $userId,
				'Review.id IS NULL',
				$this->alias . '.user_id <>' => $userId,
			)
		);

		return $this->find('all', $options);
	}
	
	/**
	 * count number of classes created by user
	 * @param int $userId
	 * @return int
	 */
	function getNumberOfClassCreateByUser($userId) {
		return $this->find('count', array(
			'conditions' => array(
				$this->alias . '.user_id' => $userId,
				$this->alias . '.del_flg' => 0
			)
		));
	}

	/**
	 * Get total course created
	 *
	 * @return integer
	 */
	function getTotalCourse() {
		$options = array(
			'recursive' => -1,
			'conditions' => array(
				$this->alias . '.del_flg' => 0
			)
		);

		return $this->find('count', $options);
	}
}